//  This file is part of MuMax, a high-performance micromagnetic simulator.
//  Copyright 2011  Arne Vansteenkiste and Ben Van de Wiele.
//  Use of this source code is governed by the GNU General Public License version 3
//  (as published by the Free Software Foundation) that can be found in the license.txt file.
//  Note that you are welcome to modify this code under the condition that you do not remove any 
//  copyright notices and prominently state that you modified it, giving a relevant date.

// This package implements automated mumax API generation.
// Based on the exported methods of engine.API, an API
// library in any of the supported programming languages is
// automatically generated.
//
// Author: Arne Vansteenkiste
package apigen

import (
	"flag"
	"fmt"
	. "mumax/common"
	"mumax/engine"
	"mumax/frontend"
	"os"
	"reflect"
	"sort"
)

// the package name
const pkg = "mumax2"

// Auto-generate API libraries for all languages.
func APIGen() {

	flag.Parse()

	headers := parseSource(flag.Arg(0))

	// interpreter can extract the methods

	method := make(map[string]reflect.Value)
	frontend.AddMethods(method, new(engine.API))

	// order by alphabetic order
	keys := make([]string, len(method))
	i := 0
	for k, _ := range method {
		keys[i] = k
		i++
	}
	sort.Strings(keys)

	// target languages
	langs := []Lang{&Python{}, &Tex{}} //, &Java{}, &Lua{}}

	// output api files for each language.
	for _, lang := range langs {
		file := lang.Filename()
		Log("Generating", file)
		out, err := os.Create(file)
		CheckErr(err, ERR_IO)

		fmt.Fprintln(out, lang.Comment(), "This file is automatically generated by mumax2-apigen. DO NOT EDIT.\n")

		lang.WriteHeader(out)

		for _, name := range keys {
			header, ok := headers[name]
			if !ok {
				panic(name)
			}
			returnTypes := make([]reflect.Type, method[name].Type().NumOut())
			for i := range returnTypes {
				returnTypes[i] = method[name].Type().Out(i)
			}
			comment := header.comment
			argnames := header.args
			//fmt.Println(name, comment, argnames)
			lang.WriteFunc(out, frontend.ConvertCase(name), comment, argnames, argTypes(method[name]), returnTypes)
		}

		lang.WriteFooter(out)

		out.Close()
	}
}

// make array with argument types of method.
func argTypes(c reflect.Value) []reflect.Type {
	types := make([]reflect.Type, c.Type().NumIn())
	for i := range types {
		types[i] = c.Type().In(i)
	}
	return types
}
